package com.vr.xuecheng.gateway.filter;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.vr.xuecheng.gateway.response.RestErrorResponse;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;

/**
 * @author hzh
 * @date 2025/03/29
 * 网关认证校验
 */
@Log4j2
@Data
@Component
@ConfigurationProperties(prefix = "whitelist")
@RequiredArgsConstructor
public class GateWayAuthFilter implements GlobalFilter, Ordered {
    private PathMatcher pathMatcher= new AntPathMatcher();
    // 读取配置的白名单路径
    private List<String> paths= Collections.emptyList();
    private final TokenStore tokenStore;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 拿到访问的url
        String requestUrl = exchange.getRequest().getURI().getPath();
        boolean isMatch = paths.stream().anyMatch(pattern->pathMatcher.match(pattern,requestUrl));
        if (isMatch){
            // 在白名单里直接放行
            return chain.filter(exchange);
        }
        // 拿到token
        String token = this.getToken(exchange);
        // 校验token是否为空
        if (StrUtil.isBlank(token)) {
            return this.buildMonoResult("没有认证，请先登录", exchange);
        }
        // 校验token是否过期
        try {
            OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
            if (oAuth2AccessToken.isExpired()) {
                return this.buildMonoResult("令牌已失效，请重新登录", exchange);
            }
        } catch (Exception e) {
            // 令牌校验失败
            log.error("认证令牌无效:{}",token);
            return this.buildMonoResult("认证令牌无效", exchange);
        }
        log.info("认证通过:{}",token);
        return chain.filter(exchange);
    }

    private Mono<Void> buildMonoResult(String message, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        return response.writeWith(Mono.fromSupplier(() -> {
            try {
                String responseJson = JSON.toJSONString(new RestErrorResponse(message));
                return response.bufferFactory()
                        // 将数据封装到响应体中
                        .wrap(responseJson.getBytes(StandardCharsets.UTF_8));
            } catch (Exception e) {
                throw new RuntimeException("Error writing response", e);
            }
        })).doOnSubscribe(sub->{
            // 在订阅时的操作，也就是实际处理请求之前
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        });
    }

    /**
     * 获取token
     */
    private String getToken(ServerWebExchange exchange) {
        String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (tokenStr == null) {
            return null;
        }
        // 拿到正确的token 形式Bearer token
        String token = tokenStr.split(" ")[1];
        if (StrUtil.isBlank(token)) {
            return null;
        }
        return token;
    }

    /**
     * 过滤的优先级，值越小，优先级越高
     */
    @Override
    public int getOrder() {
        return -100;
    }
}
